level 1 - 5
Uploading 5 crackmes under 1
Five crackmes, one writeup. Each level builds on the previous one, and by level 5 you'll get to see the layering in more and more logic. I don't agree with the difficulty rating on this crackmes, they are just too easy and i am not bragging.
Level 1: Plaintex in the freaking strings

On running strings i see the above, and there is a string password right there in plaintext.

Looking at the disassembly, the program reads input via console input, runs a length check: the password must be exactly 8 characters long (cmp r8, 8). If that passes, it calls memcmp with two arguments: $rcx pointing to our input and $rdx pointing to the stored string "password". memcmp goes into both of those memory addresses and compares the actual bytes one by one. If everything matches, ZF is set and program goes down happy path.

Password is password.
Level 2: XOR encrypted secret key

Requires secret key input, but it's not stored in plaintext this time; it has been XOR encrypted with the constant 0x5A. The encrypted bytes are sitting in the binary as a reference array of characters. To recover the actual password, we have to reverse XOR each byte back with 0x5A. I wrote this simple script to iterate through the reference array, XOR every character's ASCII value with 0x5A, and return the plaintext that results in those encrypted bytes.

Loads our input character by character (movzx ecx, byte ptr [rcx+rax]), XOR encrypts it on the fly with 0x5Ah, and then compares the result against the corresponding byte from the stored encrypted reference (cmp cl, [c8+rax], where rax is the loop counter). If they match at each position, the loop increments and continues. If any byte mismatches, program goes down the sad path - `Access Denied. For this one program encrypts our input and compares it to the already encrypted bytes.

The recovered key is reverse. XOR 0x5A back against the encrypted bytes gives us this.
Level 3: Username-dependent serial keygen

Now we have a keygen. The serial is no longer fixed; it is generated from the username you enter. Program loops through every character of the username summing up the ascii values of each character into edx. Once the loop finishes, the program proceeds to:
- Multiply
edxby the constant0x539h - XOR the result with
0x5A5Ah(another fixed constant)
The final result is compared against our serial key input (cmp [rsp+var_58], eax). If they match, ZF is set and program goes down the happy path.
serial = (sum_of_username_ascii_values * 1337) XOR 0x5A5A
For "defcon26": the ascii sum of those 8 characters is 0x2DE (734 in decimal). Multiply by 1337 and XOR 0x5A5A and you get 953989.

Username defcon26, serial 953989. Access Granted. You are a master keygenerator.
Level 4: Username + serial influence flag production
Level 4 keeps the username-driven serial concept from level 3 but adds a new layer: a flag; It's being produced even though the crackmes states it decrypts, i would not call that decyption. The flag produced (whether it is the correct flag or gibberish) depends entirely on whether your serial is right.

The serial input key must match a value derived from the username for the program to take the happy path. Once inside, the first character of the serial input is XOR'd with 0x34h, and that result feeds into the next XOR operation. Then the program loads a hardcoded 8-byte value from the code segment: AB460302110B0834. This is the value that gets XOR'd against the first character of the serial key input to produce the flag output. There is no position increment for the serialkey input so it's the same value from serial input being XOR'ed.


The happy path (loc_7FF6D381154A) prints "\n[+] Success! Your decrypted flag is: " followed by the decrypted flag. The sad path (loc_7FF6D381156B) prints "\n[-] Access Denied. Decrypted output: " followed by whatever gibberish flag comes out when the XOR runs with the wrong serial. The same sub_7FF6D3811C30 function outputs the flag in both cases, the only difference being which path you took and therefore which decrypted result is sitting in the buffer.

Wrong serial (12345678) gives you Nrqkxy< as the decrypted output. Gibberish, as expected.

Correct serial (89421 for username defcon26), and the decrypted flag is Mqrh{z?. The keys influence the flag. Get the serial right, get the real flag. Although the flag looks like gibberish too.
Level 5: Two-part key, two checks, one flag
No username this time, just two key inputs. There are two independent checks that both need to pass for the program to go down happy path.

Key Part 2 is used to produce the flag. The program moves through each character of Key Part 2's input (movzx r9d, dil, character by character), and XORs it against this 8 byte value: 9CC4CFD2C9DED4EB. Just like level4 there is no increment to advance to the next value of Key Part 2, so the program always works with the 1st character of Key Part 2 for this XOR operation. That is what produces the flag, and the flag will differ depending on whether you are on the happy path or the sad path because the key values differ.


Key Part 1 runs through a multiply-shift-compare sequence - a simple modulo check. Key Part 2 then goes through an XOR and addition before its comparison. Both checks must pass:
if (key_part_1 % 17 != 0) { deny(); }
if (key_part_2 != ((key_part_1 ^ 0x1337) + 42)) { deny(); }
Line 1: Key Part 1 must be perfectly divisible by 17. The imul ecx, edx, 11h in the assembly exposes this: 0x11h is 17 in decimal, and the compiler always multiplies back by the original divisor at the end of this trick, which is what gives it away.
Line 2: Key Part 2 is not independent; it is derived directly from Key Part 1. XOR it with 0x1337, add 42, and that is the only valid Key Part 2.
Verified: 12345672 / 17 = 726216 remainder 0, and (12345672 ^ 0x1337) + 42 = 12350079 + 42 = 12350121 which is exactly Key Part 2.

Key Part 1: 12345672, Key Part 2: 12350121. Both checks pass. [+] LICENSE VALIDATED! and the flag is B}w'{fm5.
From level 3 onwards, each new level is carrying logic forward from the previous one. Level 3 introduced the username-to-serial algorithm. Level 4 kept that but added flag decryption on top. Level 5 dropped the username entirely, split the key into two parts, stacked two independent math checks, and still used the same XOR-against-a-hardcoded-block pattern for the flag output.